home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Nebula 1
/
Nebula One.iso
/
Graphics
/
Plotting
/
Lyapunov
/
Source
/
LyapunovView.m
< prev
next >
Wrap
Text File
|
1995-06-12
|
10KB
|
339 lines
/* Generated by Interface Builder */
#import "LyapunovView.h"
#import <math.h>
#import <appkit/tiff.h>
#import <appkit/color.h>
#import <appkit/graphics.h>
#import <appkit/Control.h>
#import <appkit/Matrix.h>
#import <appkit/NXColorWell.h>
#import <dpsclient/psops.h>
#import <appkit/Application.h>
#import <dpsclient/psops.h>
#import <dpsclient/wraps.h>
@implementation LyapunovView
- initFrame:(const NXRect *)frm // designated initializer for a view
{
[super initFrame:frm];
return self;
}
- appDidInit:sender // final initialization
{
// set up default colors
fromColor = NXConvertRGBToColor(1.0, 1.0, 0.0);
toColor = NXConvertRGBToColor(0.01, 0.01, 0.0);
backColor = NXConvertRGBToColor(0.0, 0.0, 0.05);
[fromShade setColor:fromColor];
[toShade setColor:toColor];
[backShade setColor:backColor];
// set up plot type
singleDot = NO;
[plotType selectCellWithTag:singleDot];
// set up default position
xPos = 0.0; yPos = 0.0;
[[posMatrix findCellWithTag:0] setFloatValue:xPos];
[[posMatrix findCellWithTag:1] setFloatValue:yPos];
// set up default scale
xScale = 1.0; yScale = 1.0;
[[scaleMatrix findCellWithTag:0] setFloatValue:xScale];
[[scaleMatrix findCellWithTag:1] setFloatValue:yScale];
// set up default initial value
initial = 0.1;
[[initialValue findCellWithTag:0] setFloatValue:initial];
// set up default depth
deep = 4000;
[[depth findCellWithTag:0] setIntValue:deep];
// set up default pattern
[[pattern findCellWithTag:0] setStringValue:"ab"];
// set up contrast
[contrastText setFloatValue:2.0];
[contrastSlider setFloatValue:2.0];
return self;
}
- contrast:sender // adjust image contrast coeff
{
contrast = [sender floatValue];
[contrastText setFloatValue:contrast];
[contrastSlider setFloatValue:contrast];
return self;
}
- go:sender // begin calculation of image
{
register float total, t, tNew, r, a, b, expSum;
float spMul, rb, gb, bb, rd, gd, bd, rk, gk, bk;
register int ndx, x, n, pix, y, avgCnt;
NXRect pixel, line;
float log2 = log(2);
pixel.size.width = 1;
pixel.size.height = 1;
line.size.height = 1;
line.size.width = XSIZE;
line.origin.x = 0;
[self newParam:self]; // be sure we're up to date
// set up color constants
rb = NXRedComponent(fromColor);
gb = NXGreenComponent(fromColor);
bb = NXBlueComponent(fromColor);
rd = NXRedComponent(toColor) - rb;
gd = NXGreenComponent(toColor) - gb;
bd = NXBlueComponent(toColor) - bb;
rk = NXRedComponent(backColor);
gk = NXGreenComponent(backColor);
bk = NXBlueComponent(backColor);
// main calc loop: calc line & draw it.
expSum = 0; avgCnt = 0;
[self lockFocus];
for (y=0; y<YSIZE; y++) {
b = yPos + y*yScale/YSIZE; // get physical y-coord
for (x=0; x<XSIZE; x++) {
a = xPos + x*xScale/XSIZE; // get physical x-coord
total = 0.0; t = initial; ndx = 0;
for (n=0; n<deep; n++) {
r = (patternNum[ndx] ? a : b);
tNew = t * (1 - t) * r;
ndx++;
if (ndx >= patternLength) ndx = 0;
total += log(fabs(r - 2 * r * tNew)) / log2;
t = tNew;
}
space[x][y] = total / deep; // Lyapunov exponent approx.
// put into pixel map w/color
pix = (x+(YSIZE-y-1)*XSIZE)*RGB;
if (total >= 0) {
pixels[pix] = 255 * rk; // pix+RED = pix because RED=0
pixels[pix+GREEN] = 255 * gk;
pixels[pix+BLUE] = 255 * bk;
space[x][y] = 0;
} else { // no chaos, so color it
spMul = fabs(space[x][y]/contrast); // scale into color map
if (spMul > 1.0) spMul = 1.0;
if (fabs(space[x][y]) < 100000.0) {
expSum += space[x][y];
avgCnt++;
}
pixels[pix] = 255 *(spMul * rd + rb);
pixels[pix+GREEN]= 255*(spMul * gd + gb);
pixels[pix+BLUE] = 255*(spMul * bd + bb);
}
if (singleDot) {
// plot a pixel of it
pixel.origin.x = x;
pixel.origin.y = y;
NXImageBitmap(&pixel, 1, 1, 8, 3,
NX_MESHED, NX_COLORMASK,
&pixels[(x+(YSIZE-y-1)*XSIZE)*RGB],
NULL, NULL, NULL, NULL);
[window flushWindow];
NXPing();
}
}
if (!singleDot) {
// plot a line of it
line.origin.y = y;
NXImageBitmap(&line, XSIZE, 1, 8, 3,
NX_MESHED, NX_COLORMASK, &pixels[(YSIZE-y-1)*XSIZE*RGB],
NULL, NULL, NULL, NULL);
[window flushWindow];
NXPing();
}
}
[self unlockFocus];
[avgOut setFloatValue:(expSum/avgCnt)];
[reApplyButton setEnabled:YES];
return self;
}
- reApply:sender // re do coloring on plot
{
register int x, y, pix;
float rb, gb, bb, rd, gd, bd, rk, gk, bk;
float spMul;
NXRect pixel, line;
[self newParam:self]; // be sure we're up to date
// set up plotting bounds
pixel.size.width = 1; pixel.size.height = 1;
line.size.height = 1; line.size.width = XSIZE;
line.origin.x = 0;
// get color params
rb = NXRedComponent(fromColor); gb = NXGreenComponent(fromColor);
bb = NXBlueComponent(fromColor); rd = NXRedComponent(toColor) - rb;
gd = NXGreenComponent(toColor) - gb; bd = NXBlueComponent(toColor) - bb;
rk = NXRedComponent(backColor); gk = NXGreenComponent(backColor);
bk = NXBlueComponent(backColor);
// re-apply colors
[self lockFocus];
for (y=0; y<YSIZE; y++) {
for (x=0; x<XSIZE; x++) {
pix = (x+(YSIZE-y-1)*XSIZE)*RGB;
if (space[x][y] >= 0) {
pixels[pix] = 255 * rk; // pix+RED = pix because RED=0
pixels[pix+GREEN] = 255 * gk;
pixels[pix+BLUE] = 255 * bk;
} else { // no chaos, so color it
spMul = fabs(space[x][y]/contrast); // scale into color map
if (spMul > 1.0) spMul = 1.0;
pixels[pix] = 255 *(spMul * rd + rb);
pixels[pix+GREEN]= 255*(spMul * gd + gb);
pixels[pix+BLUE] = 255*(spMul * bd + bb);
}
}
// plot a line of it
line.origin.y = y;
NXImageBitmap(&line, XSIZE, 1, 8, 3,
NX_MESHED, NX_COLORMASK, &pixels[(YSIZE-y-1)*XSIZE*RGB],
NULL, NULL, NULL, NULL);
[window flushWindow];
NXPing();
}
[self unlockFocus];
return self;
}
- newParam:sender // set up new parameters for calc
{
int count;
// get colors
fromColor = [fromShade color];
toColor = [toShade color];
backColor = [backShade color];
// get plotting method (dots or lines)
singleDot = [[plotType selectedCell] tag];
// get x-y coordinates
xPos = [[posMatrix findCellWithTag:0] floatValue];
yPos = [[posMatrix findCellWithTag:1] floatValue];
// get x-y scaling
xScale = [[scaleMatrix findCellWithTag:0] floatValue];
yScale = [[scaleMatrix findCellWithTag:1] floatValue];
// get initial value for each point
initial = [[initialValue findCellWithTag:0] floatValue];
// get depth
deep = [[depth findCellWithTag:0] intValue];
// get pattern
patternString = [[pattern findCellWithTag:0] stringValue];
for(count=0; patternString[count]!='\0'; count++) {
patternNum[count] = ((patternString[count]=='a') ? 0 : 1);
}
patternLength = count;
return self;
}
- drawSelf:(NXRect *)rects :(int)rectCount // redraws the screen.
{
PSsetgray(NX_DKGRAY);
NXRectFill(&bounds);
return self;
}
- mouseDown:(NXEvent *)e // stolen from MandelView.m
/*
* We implement the mouseDown method so the user can sweep out a section of
* the view and select that as the current x,y,scale coordinates.
*/
{
int looping = YES;
int oldMask;
NXRect bbox;
NXPoint startPoint, currPoint;
double newX, newY, newDX, newDY;
NXCoord aspectRatio;
float mvX = xPos;
float mvY = yPos;
float mvDX = xScale;
float mvDY = yScale;
aspectRatio = bounds.size.height / bounds.size.width;
oldMask = [window addToEventMask:NX_MOUSEDRAGGEDMASK];
startPoint = e->location;
[self convertPoint:&startPoint fromView:nil];
NXSetRect(&bbox,startPoint.x,startPoint.y,0.0,0.0);
[self lockFocus];
while (looping) {
e=[NXApp getNextEvent:NX_MOUSEUPMASK | NX_MOUSEDRAGGEDMASK];
currPoint = e->location;
[self convertPoint:&currPoint fromView:nil];
bbox.size.width = 2*(currPoint.x - startPoint.x);
bbox.size.height = 2*(currPoint.y - startPoint.y);
/* Normalize bbox to always have positive width and height */
if (bbox.size.width < 0) bbox.size.width = -bbox.size.width;
if (bbox.size.height < 0) bbox.size.height = -bbox.size.height;
/*
* constrain the box to have the aspect ratio of the view. Choose
* whichever dimension is closer to the desired result.
*/
if ((bbox.size.height/bbox.size.width) > aspectRatio) {
bbox.size.height = bbox.size.width * aspectRatio;
} else {
bbox.size.width = bbox.size.height / aspectRatio;
}
/* The startPoint is always at the center of the bbox */
bbox.origin.x = startPoint.x - .5 * bbox.size.width;
bbox.origin.y = startPoint.y - .5 * bbox.size.height;
PSnewinstance();
if (looping = (e->type == NX_MOUSEDRAGGED)) {
PSsetinstance(YES);
PSsetgray(NX_WHITE);
NXFrameRect(&bbox);
PSsetinstance(NO);
}
}
[self unlockFocus];
[window setEventMask:oldMask];
if ((bbox.size.width > 0) && (bbox.size.height > 0)) {
/*
* At this point, bbox is in window coordinates. Set new controller
* parameters based on this view's coordinates and the bounding box.
*/
newDX = (bbox.size.width*mvDX)/bounds.size.width;
newDY = (bbox.size.height*mvDY)/bounds.size.height;
newX = ((startPoint.x + bounds.origin.x) / bounds.size.width);
newX = newX * mvDX + mvX - newDX/2;
newY = ((startPoint.y + bounds.origin.y) / bounds.size.height);
newY = newY * mvDY + mvY - newDY/2;
/*
* Note that we only update the text fields -- we
* don't update the internal instance variables. If we were to update
* the internal ivs, then the user would only get one chance with the
* mouse down method because subsequent mouse-downs would work in terms
* of the new coordinate system.
*/
[[posMatrix findCellWithTag:0] setFloatValue:newX];
[[posMatrix findCellWithTag:1] setFloatValue:newY];
[[scaleMatrix findCellWithTag:0] setFloatValue:newDX];
[[scaleMatrix findCellWithTag:1] setFloatValue:newDY];
}
return self;
}
@end